/*
 * This file is part of aion-unique <aion-unique.org>.
 *
 *  aion-unique is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  aion-unique is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with aion-unique.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.aionemu.gameserver.controllers;

import java.util.Collection;
import java.util.Iterator;
import java.util.List;

import javolution.util.FastList;

import org.apache.commons.lang.ArrayUtils;

import com.aionemu.gameserver.controllers.attack.AttackResult;
import com.aionemu.gameserver.controllers.attack.AttackStatus;
import com.aionemu.gameserver.controllers.movement.ActionObserver;
import com.aionemu.gameserver.controllers.movement.ActionObserver.ObserverType;
import com.aionemu.gameserver.controllers.movement.AttackCalcObserver;
import com.aionemu.gameserver.model.gameobjects.Creature;
import com.aionemu.gameserver.model.gameobjects.Item;
import com.aionemu.gameserver.model.gameobjects.player.Player;
import com.aionemu.gameserver.skillengine.model.Skill;

/**
 * @author ATracer
 * @author Cura
 */
public class ObserveController
{

	protected ActionObserver[]		observers			= new ActionObserver[0];
	protected Collection<ActionObserver>		onceUsedobservers	= new FastList<ActionObserver>().shared();
	protected AttackCalcObserver[]	attackCalcObservers	= new AttackCalcObserver[0];

	/**
	 * Once used observer add to observerController.
	 * If observer notify will be removed.
	 * @param observer
	 */
	public void attach(ActionObserver observer)
	{
		onceUsedobservers.add(observer);
	}

	/**
	 * notify that creature moved
	 */
	public void notifyMoveObservers()
	{
		notifyObservers(ObserverType.MOVE);
	}
	
	/**
	 * notify that creature moved
	 */
	public void notifyObservers(ObserverType type, Object... object)
	{
		for (Iterator<ActionObserver> iter = onceUsedobservers.iterator(); iter.hasNext();)
		{
			ActionObserver observer = iter.next();
			if (ArrayUtils.contains(observer.getObserverType(), type))
			{
				iter.remove();
				notifyAction(type, observer, object);
			}
		}

		synchronized (observers)
		{
			for (ActionObserver observer : observers)
			{
				if (ArrayUtils.contains(observer.getObserverType(), type))
					notifyAction(type, observer, object);
			}
		}
	}

	private void notifyAction(ObserverType type, ActionObserver observer, Object... object)
	{
		switch (type)
		{
			case ATTACK:
				observer.attack((Creature)object[0]);
				break;
			case ATTACKED:
				observer.attacked((Creature)object[0]);
				break;
			case EQUIP:
				observer.equip((Item)object[0], (Player)object[1]);
				break;
			case UN_EQUIP:
				observer.unequip((Item)object[0], (Player)object[1]);
				break;
			case MOVE:
				observer.moved();
				break;
			case SKILLUSE:
				observer.skilluse((Skill)object[0]);
				break;
		}
	}
	/**
	 * notify that creature attacking
	 */
	public void notifyAttackObservers(Creature creature)
	{
		notifyObservers(ObserverType.ATTACK, creature);
	}

	/**
	 * notify that creature attacked
	 */
	public void notifyAttackedObservers(Creature creature)
	{
		notifyObservers(ObserverType.ATTACKED, creature);
	}

	/**
	 * notify that creature used a skill
	 */
	public void notifySkilluseObservers(Skill skill)
	{
		notifyObservers(ObserverType.SKILLUSE, skill);
	}

	/**
	 * 
	 * @param item
	 * @param owner
	 */
	public void notifyItemEquip(Item item, Player owner)
	{
		notifyObservers(ObserverType.EQUIP, item , owner);
	}

	/**
	 * 
	 * @param item
	 * @param owner
	 */
	public void notifyItemUnEquip(Item item, Player owner)
	{
		notifyObservers(ObserverType.UN_EQUIP, item , owner);
	}

	/**
	 * 
	 * @param observer
	 */
	public void addObserver(ActionObserver observer)
	{
		synchronized (observers)
		{
			observers = (ActionObserver[]) ArrayUtils.add(observers, observer);
		}
	}

	/**
	 * 
	 * @param observer
	 */
	public void addAttackCalcObserver(AttackCalcObserver observer)
	{
		synchronized (attackCalcObservers)
		{
			attackCalcObservers = (AttackCalcObserver[]) ArrayUtils.add(attackCalcObservers, observer);
		}
	}

	/**
	 * 
	 * @param observer
	 */
	public void removeObserver(ActionObserver observer)
	{
		synchronized (observers)
		{
			observers = (ActionObserver[]) ArrayUtils.removeElement(observers, observer);
		}
		onceUsedobservers.remove(observer);
	}

	/**
	 * 
	 * @param observer
	 */
	public void removeAttackCalcObserver(AttackCalcObserver observer)
	{
		synchronized (attackCalcObservers)
		{
			attackCalcObservers = (AttackCalcObserver[]) ArrayUtils.removeElement(attackCalcObservers, observer);
		}
	}

	/**
	 * 
	 * @param status
	 * @return true or false
	 */
	public boolean checkAttackStatus(AttackStatus status)
	{
		synchronized (attackCalcObservers)
		{
			for (AttackCalcObserver observer : attackCalcObservers)
			{
				if (observer.checkStatus(status))
					return true;
			}
		}
		return false;
	}

	/**
	 * 
	 * @param status
	 * @return
	 */
	public boolean checkAttackerStatus(AttackStatus status)
	{
		synchronized (attackCalcObservers)
		{
			for (AttackCalcObserver observer : attackCalcObservers)
			{
				if (observer.checkAttackerStatus(status))
					return true;
			}
		}
		return false;
	}

	/**
	 * @param attackList
	 */
	public void checkShieldStatus(List<AttackResult> attackList)
	{
		synchronized (attackCalcObservers)
		{
			for (AttackCalcObserver observer : attackCalcObservers)
			{
				observer.checkShield(attackList);
			}
		}
	}
}
